Analyze and visualize the food inflation and wage growth in Indonesia from 2010 to 2021.

1. Install and load the necessary library.

library(RMySQL)
Loading required package: DBI
library(tidyverse)
── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6     ✔ purrr   0.3.4
✔ tibble  3.1.7     ✔ dplyr   1.0.9
✔ tidyr   1.2.0     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(DT)
library(gganimate)
library(ggimage)

RMySQL package is needed to connect to MySQL database. Tidyverse package is used for data processing and visualization. DT improves the quality of the table display on the HTML page.

2. Connect to local MySQL server.

mydb <- dbConnect(RMySQL::MySQL(),
                 dbname='commodity_price',
                 host='localhost',
                 port=3306,
                 user = user,
                 password = password)

3. Show the tables inside the database.

dbListTables(mydb)
[1] "food_commodity_price" "minimum_wage"        
food_commodity_price

minimum_wage

The raw data for food commodity price can be downloaded on https://aplikasi2.pertanian.go.id/simharga2017/konsumen_kota/hkp4. The raw data for minimum wage (except for 2017 and 2021) can be downloaded on https://www.bps.go.id/indicator/19/220/1/upah-minimum-regional-propinsi.html. The average minimum wage for 2017 and 2021 is calculated from https://finance.detik.com/berita-ekonomi-bisnis/d-3356762/ini-dia-daftar-lengkap-ump-2017-di-34-provinsi and https://finance.detik.com/berita-ekonomi-bisnis/d-5326918/lengkap-rincian-ump-2021-di-34-provinsi.

4. Write a query to select all rows and columns from the food_commodity_price and minimum_wage table, then show the results.

result_1 <- dbGetQuery(mydb, "SELECT * FROM food_commodity_price")

datatable(
  result_1,
  extensions = "FixedColumns",
  options = list(
    paging = TRUE, searching = TRUE,
    scrollX = TRUE, fixedColumns = list(leftColumns = 2)
  )
)
result_2 <- dbGetQuery(mydb, "SELECT * FROM minimum_wage")

datatable(
  result_2,
  extensions = "FixedColumns",
  options = list(
    paging = TRUE, searching = TRUE,
    scrollX = TRUE, fixedColumns = list(leftColumns = 2)
  )
)

5. The data for food commodity price are written in wide format, so let’s convert the data into long format, then save the data as a view.

dbGetQuery(mydb, "DROP VIEW IF EXISTS fcp_long")
dbGetQuery(mydb, "CREATE VIEW fcp_long AS
                           SELECT `Komoditas Pangan` AS food_commodity, '2010' AS year, `2010` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2011' AS year, `2011` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2012' AS year, `2012` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2013' AS year, `2013` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2014' AS year, `2014` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2015' AS year, `2015` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2016' AS year, `2016` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2017' AS year, `2017` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2018' AS year, `2018` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2019' AS year, `2019` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2020' AS year, `2020` AS price_per_kg
                           FROM food_commodity_price UNION ALL
                           SELECT `Komoditas Pangan` AS food_commodity, '2021' AS year, `2021` AS price_per_kg
                           FROM food_commodity_price
                           ORDER BY food_commodity, year")

6. Show the results of the view.

result_3 <- dbGetQuery(mydb, "SELECT * FROM fcp_long")

datatable(
  result_3,
  extensions = 'Scroller',
  options = list(
    deferRender = TRUE,
    scrollY = 200,
    scroller = TRUE
  )
)

7. For visualization purpose, the food commodity with any null value in the price column will be excluded.

result_4 <- dbGetQuery(mydb, "SELECT food_commodity FROM fcp_long GROUP BY 1 HAVING MIN(price_per_kg) <> 0")
result_4

The following food commodities have no null value in the price column: shallot (bawang merah), rice (beras), red chili (cabe merah), curly red chili (cabe merah keriting), chicken meat (daging ayam broiler), beef (daging sapi), white sugar (gula pasir lokal), cooking oil (minyak goreng kemasan), and chicken egg (telur ayam ras). I am going to exclude curly red chili because it’s very similar to red chili.

8. Combine the minimum wage data with the food price using UNION. Then calculate the YoY and cumulative growth percentage for food’s price and minimum wage.

food_wage_df <- dbGetQuery(mydb, "WITH food_wage AS (SELECT *
                  FROM fcp_long
                  WHERE food_commodity <> 'Cabe Merah Keriting'
                    AND food_commodity IN (SELECT food_commodity
                                           FROM fcp_long
                                           GROUP BY 1
                                           HAVING MIN(price_per_kg) <> 0)
                  UNION
                  SELECT nama_variabel, nama_tahun, data_content
                  FROM minimum_wage
                  ORDER BY 1, 2)
                  SELECT food_commodity AS food_or_wage, CAST(year AS SIGNED) as year,
                        price_per_kg AS price_per_kg_or_minimum_wage,
                        (price_per_kg - (LAG(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year))) /
                        (LAG(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year)) * 100 AS growth_percentage,
                        (price_per_kg - (FIRST_VALUE(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year))) /
                        (FIRST_VALUE(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year)) * 100 AS cumulative_gp
                  FROM food_wage")
Warning in .local(conn, statement, ...): Decimal MySQL column 3 imported as
numeric
Warning in .local(conn, statement, ...): Decimal MySQL column 4 imported as
numeric
datatable(
  food_wage_df,
  extensions = 'Scroller',
  options = list(
    deferRender = TRUE,
    scrollY = 200,
    scroller = TRUE
  )
)

CAST function is used to change the year to signed integer. This allows R to save them as dbl (double-precision floating point number) instead of chr (characters).

9. Visualize the cumulative growth percentage for food’s price and minimum wage since 2010.

food_wage_df[is.na(food_wage_df)] <- 0
food_wage_df %>%
  ggplot(aes(cumulative_gp, food_or_wage)) +
  geom_col(aes(fill = food_or_wage)) +
  facet_wrap(vars(year))

Make a new dataframe to rank the food commodities and wage based on the cumulative percent growth for each year, and translate the items from Indonesian to English.

food_wage_df2 <- food_wage_df %>%
  group_by(year) %>%
  arrange(year, desc(cumulative_gp)) %>%
  mutate(ranking = row_number())

food_wage_df2[food_wage_df2 == "Upah Minimum Regional/Propinsi"] <- "Min Wage"
food_wage_df2[food_wage_df2 == "Daging Ayam Broiler"] <- "Chicken"
food_wage_df2[food_wage_df2 == "Cabe Merah"] <- "Red Chili"
food_wage_df2[food_wage_df2 == "Bawang Merah"] <- "Shallot"
food_wage_df2[food_wage_df2 == "Telur Ayam Ras"] <- "Egg"
food_wage_df2[food_wage_df2 == "Gula Pasir Lokal"] <- "Sugar"
food_wage_df2[food_wage_df2 == "Minyak Goreng Kemasan"] <- "Cooking Oil"
food_wage_df2[food_wage_df2 == "Beras"] <- "Rice"
food_wage_df2[food_wage_df2 == "Daging Sapi"] <- "Beef"

datatable(
  food_wage_df2,
  extensions = 'Scroller',
  options = list(
    deferRender = TRUE,
    scrollY = 200,
    scroller = TRUE
  )
)

Make a new dataframe which contain the image path for each item, which will be used for visualization. All of the images are downloaded from freepik.com under free license. Beef, egg, and chicken are designed by Terdpongvector. Rice, red chili, and shallot are designed by macrovector. Cooking oil is designed by valadzionak_volha. Sugar is desgined by pch.vector. Wage is desgined by katemangostar.

img_df <- data.frame(food_or_wage = c("Min Wage", "Chicken", "Red Chili", "Shallot", "Egg", "Sugar", "Cooking Oil", "Rice", "Beef"),
                     image = c("C:/Users/Marselo/Downloads/food images/wage.png",
                               "C:/Users/Marselo/Downloads/food images/chicken.png",
                               "C:/Users/Marselo/Downloads/food images/red_chili.png",
                               "C:/Users/Marselo/Downloads/food images/shallot.png",
                               "C:/Users/Marselo/Downloads/food images/egg.png",
                               "C:/Users/Marselo/Downloads/food images/sugar.png",
                               "C:/Users/Marselo/Downloads/food images/cooking_oil.png",
                               "C:/Users/Marselo/Downloads/food images/rice.png",
                               "C:/Users/Marselo/Downloads/food images/beef.png"),
                               stringsAsFactors = F)
img_df
static_plot <- food_wage_df2 %>%
  left_join(img_df, by = "food_or_wage") %>%
  ggplot(aes(ranking, cumulative_gp)) +
  geom_col(aes(fill = food_or_wage), alpha=0.7) +
  coord_flip(clip = "off", expand = FALSE) +
  scale_x_reverse() +
  scale_y_continuous(labels = function(x) paste0(x, '%'))+
  geom_text(aes(ranking, cumulative_gp, label = paste0(scales::number(cumulative_gp, acc = 0.1), "% ")),
            hjust = ifelse(food_wage_df2$cumulative_gp < 0, 0, 1), vjust=0.75, size=5) +
  geom_text(aes(ranking, cumulative_gp, label = paste0(food_or_wage," ")),
            hjust = ifelse(food_wage_df2$cumulative_gp < 0, 0, 1), vjust=-0.75, size=5, fontface="bold") +
  geom_image(aes(ranking, y=-40, image = image),
             hjust = 0, size=0.08,) +
  labs(title='Cumulative Food Inflation and Wage Growth in Indonesia',
       subtitle = '2010-{closest_state}', x = "",
       y = "Cumulative Percentage Change",
       caption = 'Data Source: aplikasi2.pertanian.go.id & bps.go.id. Image Source: freepik.com.') +
  theme_minimal() +
  theme(
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.title.x = element_text(size=12),
    plot.title = element_text(hjust = 0.5, size = 24),
    plot.subtitle = element_text(hjust = 0.5, size = 24),
    plot.caption = element_text(hjust = 0.5, color='gray', size=10),
    plot.margin = margin(1,1,1,1, "cm"),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  guides(fill = "none")

animated_plot <- static_plot + transition_states(year, transition_length = 4, state_length = 1) +
  ease_aes('cubic-in-out')

gif_animation <- animate(animated_plot,100,fps = 20,duration = 10, width = 1000, height = 1000, renderer = gifski_renderer())
gif_animation

anim_save("food_wage.gif",animation = gif_animation)

From the visualization, we can see that the minimum wage in Indonesia has a significant increase compared to the basic food prices from 2010 to 2021. This means a minimum wage on 2021 has more food purchasing power compared to 2010.

LS0tDQp0aXRsZTogIkZvb2QgSW5mbGF0aW9uIGFuZCBXYWdlIEdyb3d0aCBpbiBJbmRvbmVzaWEiDQphdXRob3I6ICJUaW1vdGl1cyBNYXJzZWxvIg0KZGF0ZTogMjcvMDgvMjAyMg0Kb3V0cHV0Og0KICAgIGh0bWxfbm90ZWJvb2s6DQogICAgICAgIHRvYzogVFJVRQ0KLS0tDQoNCiMgQW5hbHl6ZSBhbmQgdmlzdWFsaXplIHRoZSBmb29kIGluZmxhdGlvbiBhbmQgd2FnZSBncm93dGggaW4gSW5kb25lc2lhIGZyb20gMjAxMCB0byAyMDIxLg0KDQojIyAxLiBJbnN0YWxsIGFuZCBsb2FkIHRoZSBuZWNlc3NhcnkgbGlicmFyeS4NCg0KYGBge3J9DQpsaWJyYXJ5KFJNeVNRTCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShnZ2ltYWdlKQ0KYGBgDQoNClJNeVNRTCBwYWNrYWdlIGlzIG5lZWRlZCB0byBjb25uZWN0IHRvIE15U1FMIGRhdGFiYXNlLiBUaWR5dmVyc2UgcGFja2FnZSBpcyB1c2VkIGZvciBkYXRhIHByb2Nlc3NpbmcgYW5kIHZpc3VhbGl6YXRpb24uIERUIGltcHJvdmVzIHRoZSBxdWFsaXR5IG9mIHRoZSB0YWJsZSBkaXNwbGF5IG9uIHRoZSBIVE1MIHBhZ2UuDQoNCmBgYHtyIGVjaG8gPSBGQUxTRX0NCnVzZXIgPC0gInJvb3QiDQpwYXNzd29yZCA8LSAicm9vdCINCmBgYA0KDQojIyAyLiBDb25uZWN0IHRvIGxvY2FsIE15U1FMIHNlcnZlci4NCg0KYGBge3J9DQpteWRiIDwtIGRiQ29ubmVjdChSTXlTUUw6Ok15U1FMKCksDQogICAgICAgICAgICAgICAgIGRibmFtZT0nY29tbW9kaXR5X3ByaWNlJywNCiAgICAgICAgICAgICAgICAgaG9zdD0nbG9jYWxob3N0JywNCiAgICAgICAgICAgICAgICAgcG9ydD0zMzA2LA0KICAgICAgICAgICAgICAgICB1c2VyID0gdXNlciwNCiAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSBwYXNzd29yZCkNCmBgYA0KDQojIyAzLiBTaG93IHRoZSB0YWJsZXMgaW5zaWRlIHRoZSBkYXRhYmFzZS4NCg0KYGBge3J9DQpkYkxpc3RUYWJsZXMobXlkYikNCmBgYA0KVGhlIHJhdyBkYXRhIGZvciBmb29kIGNvbW1vZGl0eSBwcmljZSBjYW4gYmUgZG93bmxvYWRlZCBvbiBodHRwczovL2FwbGlrYXNpMi5wZXJ0YW5pYW4uZ28uaWQvc2ltaGFyZ2EyMDE3L2tvbnN1bWVuX2tvdGEvaGtwNC4gVGhlIHJhdyBkYXRhIGZvciBtaW5pbXVtIHdhZ2UgKGV4Y2VwdCBmb3IgMjAxNyBhbmQgMjAyMSkgY2FuIGJlIGRvd25sb2FkZWQgb24gaHR0cHM6Ly93d3cuYnBzLmdvLmlkL2luZGljYXRvci8xOS8yMjAvMS91cGFoLW1pbmltdW0tcmVnaW9uYWwtcHJvcGluc2kuaHRtbC4gVGhlIGF2ZXJhZ2UgbWluaW11bSB3YWdlIGZvciAyMDE3IGFuZCAyMDIxIGlzIGNhbGN1bGF0ZWQgZnJvbSBodHRwczovL2ZpbmFuY2UuZGV0aWsuY29tL2Jlcml0YS1la29ub21pLWJpc25pcy9kLTMzNTY3NjIvaW5pLWRpYS1kYWZ0YXItbGVuZ2thcC11bXAtMjAxNy1kaS0zNC1wcm92aW5zaSBhbmQgaHR0cHM6Ly9maW5hbmNlLmRldGlrLmNvbS9iZXJpdGEtZWtvbm9taS1iaXNuaXMvZC01MzI2OTE4L2xlbmdrYXAtcmluY2lhbi11bXAtMjAyMS1kaS0zNC1wcm92aW5zaS4NCg0KIyMgNC4gV3JpdGUgYSBxdWVyeSB0byBzZWxlY3QgYWxsIHJvd3MgYW5kIGNvbHVtbnMgZnJvbSB0aGUgZm9vZF9jb21tb2RpdHlfcHJpY2UgYW5kIG1pbmltdW1fd2FnZSB0YWJsZSwgdGhlbiBzaG93IHRoZSByZXN1bHRzLg0KDQpgYGB7cn0NCnJlc3VsdF8xIDwtIGRiR2V0UXVlcnkobXlkYiwgIlNFTEVDVCAqIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UiKQ0KDQpkYXRhdGFibGUoDQogIHJlc3VsdF8xLA0KICBleHRlbnNpb25zID0gIkZpeGVkQ29sdW1ucyIsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIHBhZ2luZyA9IFRSVUUsIHNlYXJjaGluZyA9IFRSVUUsDQogICAgc2Nyb2xsWCA9IFRSVUUsIGZpeGVkQ29sdW1ucyA9IGxpc3QobGVmdENvbHVtbnMgPSAyKQ0KICApDQopDQpgYGANCg0KYGBge3J9DQpyZXN1bHRfMiA8LSBkYkdldFF1ZXJ5KG15ZGIsICJTRUxFQ1QgKiBGUk9NIG1pbmltdW1fd2FnZSIpDQoNCmRhdGF0YWJsZSgNCiAgcmVzdWx0XzIsDQogIGV4dGVuc2lvbnMgPSAiRml4ZWRDb2x1bW5zIiwNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgcGFnaW5nID0gVFJVRSwgc2VhcmNoaW5nID0gVFJVRSwNCiAgICBzY3JvbGxYID0gVFJVRSwgZml4ZWRDb2x1bW5zID0gbGlzdChsZWZ0Q29sdW1ucyA9IDIpDQogICkNCikNCmBgYA0KDQojIyA1LiBUaGUgZGF0YSBmb3IgZm9vZCBjb21tb2RpdHkgcHJpY2UgYXJlIHdyaXR0ZW4gaW4gd2lkZSBmb3JtYXQsIHNvIGxldCdzIGNvbnZlcnQgdGhlIGRhdGEgaW50byBsb25nIGZvcm1hdCwgdGhlbiBzYXZlIHRoZSBkYXRhIGFzIGEgdmlldy4NCg0KYGBge3J9DQpkYkdldFF1ZXJ5KG15ZGIsICJEUk9QIFZJRVcgSUYgRVhJU1RTIGZjcF9sb25nIikNCmRiR2V0UXVlcnkobXlkYiwgIkNSRUFURSBWSUVXIGZjcF9sb25nIEFTDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxMCcgQVMgeWVhciwgYDIwMTBgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDExJyBBUyB5ZWFyLCBgMjAxMWAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTInIEFTIHllYXIsIGAyMDEyYCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxMycgQVMgeWVhciwgYDIwMTNgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDE0JyBBUyB5ZWFyLCBgMjAxNGAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTUnIEFTIHllYXIsIGAyMDE1YCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxNicgQVMgeWVhciwgYDIwMTZgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDE3JyBBUyB5ZWFyLCBgMjAxN2AgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTgnIEFTIHllYXIsIGAyMDE4YCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxOScgQVMgeWVhciwgYDIwMTlgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDIwJyBBUyB5ZWFyLCBgMjAyMGAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMjEnIEFTIHllYXIsIGAyMDIxYCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE9SREVSIEJZIGZvb2RfY29tbW9kaXR5LCB5ZWFyIikNCmBgYA0KDQojIyA2LiBTaG93IHRoZSByZXN1bHRzIG9mIHRoZSB2aWV3Lg0KDQpgYGB7cn0NCnJlc3VsdF8zIDwtIGRiR2V0UXVlcnkobXlkYiwgIlNFTEVDVCAqIEZST00gZmNwX2xvbmciKQ0KDQpkYXRhdGFibGUoDQogIHJlc3VsdF8zLA0KICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgZGVmZXJSZW5kZXIgPSBUUlVFLA0KICAgIHNjcm9sbFkgPSAyMDAsDQogICAgc2Nyb2xsZXIgPSBUUlVFDQogICkNCikNCmBgYA0KDQoNCiMjIDcuIEZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2UsIHRoZSBmb29kIGNvbW1vZGl0eSB3aXRoIGFueSBudWxsIHZhbHVlIGluIHRoZSBwcmljZSBjb2x1bW4gd2lsbCBiZSBleGNsdWRlZC4NCmBgYHtyfQ0KcmVzdWx0XzQgPC0gZGJHZXRRdWVyeShteWRiLCAiU0VMRUNUIGZvb2RfY29tbW9kaXR5IEZST00gZmNwX2xvbmcgR1JPVVAgQlkgMSBIQVZJTkcgTUlOKHByaWNlX3Blcl9rZykgPD4gMCIpDQpyZXN1bHRfNA0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZm9vZCBjb21tb2RpdGllcyBoYXZlIG5vIG51bGwgdmFsdWUgaW4gdGhlIHByaWNlIGNvbHVtbjogc2hhbGxvdCAoYmF3YW5nIG1lcmFoKSwgcmljZSAoYmVyYXMpLCByZWQgY2hpbGkgKGNhYmUgbWVyYWgpLCBjdXJseSByZWQgY2hpbGkgKGNhYmUgbWVyYWgga2VyaXRpbmcpLCBjaGlja2VuIG1lYXQgKGRhZ2luZyBheWFtIGJyb2lsZXIpLCBiZWVmIChkYWdpbmcgc2FwaSksIHdoaXRlIHN1Z2FyIChndWxhIHBhc2lyIGxva2FsKSwgY29va2luZyBvaWwgKG1pbnlhayBnb3Jlbmcga2VtYXNhbiksIGFuZCBjaGlja2VuIGVnZyAodGVsdXIgYXlhbSByYXMpLiBJIGFtIGdvaW5nIHRvIGV4Y2x1ZGUgY3VybHkgcmVkIGNoaWxpIGJlY2F1c2UgaXQncyB2ZXJ5IHNpbWlsYXIgdG8gcmVkIGNoaWxpLg0KDQojIyA4LiBDb21iaW5lIHRoZSBtaW5pbXVtIHdhZ2UgZGF0YSB3aXRoIHRoZSBmb29kIHByaWNlIHVzaW5nIFVOSU9OLiBUaGVuIGNhbGN1bGF0ZSB0aGUgWW9ZIGFuZCBjdW11bGF0aXZlIGdyb3d0aCBwZXJjZW50YWdlIGZvciBmb29kJ3MgcHJpY2UgYW5kIG1pbmltdW0gd2FnZS4NCmBgYHtyfQ0KZm9vZF93YWdlX2RmIDwtIGRiR2V0UXVlcnkobXlkYiwgIldJVEggZm9vZF93YWdlIEFTIChTRUxFQ1QgKg0KICAgICAgICAgICAgICAgICAgRlJPTSBmY3BfbG9uZw0KICAgICAgICAgICAgICAgICAgV0hFUkUgZm9vZF9jb21tb2RpdHkgPD4gJ0NhYmUgTWVyYWggS2VyaXRpbmcnDQogICAgICAgICAgICAgICAgICAgIEFORCBmb29kX2NvbW1vZGl0eSBJTiAoU0VMRUNUIGZvb2RfY29tbW9kaXR5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmY3BfbG9uZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIQVZJTkcgTUlOKHByaWNlX3Blcl9rZykgPD4gMCkNCiAgICAgICAgICAgICAgICAgIFVOSU9ODQogICAgICAgICAgICAgICAgICBTRUxFQ1QgbmFtYV92YXJpYWJlbCwgbmFtYV90YWh1biwgZGF0YV9jb250ZW50DQogICAgICAgICAgICAgICAgICBGUk9NIG1pbmltdW1fd2FnZQ0KICAgICAgICAgICAgICAgICAgT1JERVIgQlkgMSwgMikNCiAgICAgICAgICAgICAgICAgIFNFTEVDVCBmb29kX2NvbW1vZGl0eSBBUyBmb29kX29yX3dhZ2UsIENBU1QoeWVhciBBUyBTSUdORUQpIGFzIHllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICBwcmljZV9wZXJfa2cgQVMgcHJpY2VfcGVyX2tnX29yX21pbmltdW1fd2FnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIChwcmljZV9wZXJfa2cgLSAoTEFHKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSkgLw0KICAgICAgICAgICAgICAgICAgICAgICAgKExBRyhwcmljZV9wZXJfa2cpIE9WRVIgKFBBUlRJVElPTiBCWSBmb29kX2NvbW1vZGl0eSBPUkRFUiBCWSB5ZWFyKSkgKiAxMDAgQVMgZ3Jvd3RoX3BlcmNlbnRhZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAocHJpY2VfcGVyX2tnIC0gKEZJUlNUX1ZBTFVFKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSkgLw0KICAgICAgICAgICAgICAgICAgICAgICAgKEZJUlNUX1ZBTFVFKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSAqIDEwMCBBUyBjdW11bGF0aXZlX2dwDQogICAgICAgICAgICAgICAgICBGUk9NIGZvb2Rfd2FnZSIpDQoNCmRhdGF0YWJsZSgNCiAgZm9vZF93YWdlX2RmLA0KICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgZGVmZXJSZW5kZXIgPSBUUlVFLA0KICAgIHNjcm9sbFkgPSAyMDAsDQogICAgc2Nyb2xsZXIgPSBUUlVFDQogICkNCikNCmBgYA0KDQpDQVNUIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY2hhbmdlIHRoZSB5ZWFyIHRvIHNpZ25lZCBpbnRlZ2VyLiBUaGlzIGFsbG93cyBSIHRvIHNhdmUgdGhlbSBhcyBkYmwgKGRvdWJsZS1wcmVjaXNpb24gZmxvYXRpbmcgcG9pbnQgbnVtYmVyKSBpbnN0ZWFkIG9mIGNociAoY2hhcmFjdGVycykuDQoNCiMjIDkuIFZpc3VhbGl6ZSB0aGUgY3VtdWxhdGl2ZSBncm93dGggcGVyY2VudGFnZSBmb3IgZm9vZCdzIHByaWNlIGFuZCBtaW5pbXVtIHdhZ2Ugc2luY2UgMjAxMC4NCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTR9DQpmb29kX3dhZ2VfZGZbaXMubmEoZm9vZF93YWdlX2RmKV0gPC0gMA0KZm9vZF93YWdlX2RmICU+JQ0KICBnZ3Bsb3QoYWVzKGN1bXVsYXRpdmVfZ3AsIGZvb2Rfb3Jfd2FnZSkpICsNCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBmb29kX29yX3dhZ2UpKSArDQogIGZhY2V0X3dyYXAodmFycyh5ZWFyKSkNCmBgYA0KDQpNYWtlIGEgbmV3IGRhdGFmcmFtZSB0byByYW5rIHRoZSBmb29kIGNvbW1vZGl0aWVzIGFuZCB3YWdlIGJhc2VkIG9uIHRoZSBjdW11bGF0aXZlIHBlcmNlbnQgZ3Jvd3RoIGZvciBlYWNoIHllYXIsIGFuZCB0cmFuc2xhdGUgdGhlIGl0ZW1zIGZyb20gSW5kb25lc2lhbiB0byBFbmdsaXNoLg0KDQpgYGB7cn0NCmZvb2Rfd2FnZV9kZjIgPC0gZm9vZF93YWdlX2RmICU+JQ0KICBncm91cF9ieSh5ZWFyKSAlPiUNCiAgYXJyYW5nZSh5ZWFyLCBkZXNjKGN1bXVsYXRpdmVfZ3ApKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSByb3dfbnVtYmVyKCkpDQoNCmZvb2Rfd2FnZV9kZjJbZm9vZF93YWdlX2RmMiA9PSAiVXBhaCBNaW5pbXVtIFJlZ2lvbmFsL1Byb3BpbnNpIl0gPC0gIk1pbiBXYWdlIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJEYWdpbmcgQXlhbSBCcm9pbGVyIl0gPC0gIkNoaWNrZW4iDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkNhYmUgTWVyYWgiXSA8LSAiUmVkIENoaWxpIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJCYXdhbmcgTWVyYWgiXSA8LSAiU2hhbGxvdCINCmZvb2Rfd2FnZV9kZjJbZm9vZF93YWdlX2RmMiA9PSAiVGVsdXIgQXlhbSBSYXMiXSA8LSAiRWdnIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJHdWxhIFBhc2lyIExva2FsIl0gPC0gIlN1Z2FyIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJNaW55YWsgR29yZW5nIEtlbWFzYW4iXSA8LSAiQ29va2luZyBPaWwiDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkJlcmFzIl0gPC0gIlJpY2UiDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkRhZ2luZyBTYXBpIl0gPC0gIkJlZWYiDQoNCmRhdGF0YWJsZSgNCiAgZm9vZF93YWdlX2RmMiwNCiAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIGRlZmVyUmVuZGVyID0gVFJVRSwNCiAgICBzY3JvbGxZID0gMjAwLA0KICAgIHNjcm9sbGVyID0gVFJVRQ0KICApDQopDQpgYGANCg0KTWFrZSBhIG5ldyBkYXRhZnJhbWUgd2hpY2ggY29udGFpbiB0aGUgaW1hZ2UgcGF0aCBmb3IgZWFjaCBpdGVtLCB3aGljaCB3aWxsIGJlIHVzZWQgZm9yIHZpc3VhbGl6YXRpb24uIEFsbCBvZiB0aGUgaW1hZ2VzIGFyZSBkb3dubG9hZGVkIGZyb20gZnJlZXBpay5jb20gdW5kZXIgZnJlZSBsaWNlbnNlLiBCZWVmLCBlZ2csIGFuZCBjaGlja2VuIGFyZSBkZXNpZ25lZCBieSBUZXJkcG9uZ3ZlY3Rvci4gUmljZSwgcmVkIGNoaWxpLCBhbmQgc2hhbGxvdCBhcmUgZGVzaWduZWQgYnkgbWFjcm92ZWN0b3IuIENvb2tpbmcgb2lsIGlzIGRlc2lnbmVkIGJ5IHZhbGFkemlvbmFrX3ZvbGhhLiBTdWdhciBpcyBkZXNnaW5lZCBieSBwY2gudmVjdG9yLiBXYWdlIGlzIGRlc2dpbmVkIGJ5IGthdGVtYW5nb3N0YXIuDQoNCmBgYHtyfQ0KaW1nX2RmIDwtIGRhdGEuZnJhbWUoZm9vZF9vcl93YWdlID0gYygiTWluIFdhZ2UiLCAiQ2hpY2tlbiIsICJSZWQgQ2hpbGkiLCAiU2hhbGxvdCIsICJFZ2ciLCAiU3VnYXIiLCAiQ29va2luZyBPaWwiLCAiUmljZSIsICJCZWVmIiksDQogICAgICAgICAgICAgICAgICAgICBpbWFnZSA9IGMoIkM6L1VzZXJzL01hcnNlbG8vRG93bmxvYWRzL2Zvb2QgaW1hZ2VzL3dhZ2UucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvY2hpY2tlbi5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9yZWRfY2hpbGkucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvc2hhbGxvdC5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9lZ2cucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvc3VnYXIucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvY29va2luZ19vaWwucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvcmljZS5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9iZWVmLnBuZyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQ0KaW1nX2RmDQpgYGANCg0KYGBge3J9DQpzdGF0aWNfcGxvdCA8LSBmb29kX3dhZ2VfZGYyICU+JQ0KICBsZWZ0X2pvaW4oaW1nX2RmLCBieSA9ICJmb29kX29yX3dhZ2UiKSAlPiUNCiAgZ2dwbG90KGFlcyhyYW5raW5nLCBjdW11bGF0aXZlX2dwKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbCA9IGZvb2Rfb3Jfd2FnZSksIGFscGhhPTAuNykgKw0KICBjb29yZF9mbGlwKGNsaXAgPSAib2ZmIiwgZXhwYW5kID0gRkFMU0UpICsNCiAgc2NhbGVfeF9yZXZlcnNlKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKHgsICclJykpKw0KICBnZW9tX3RleHQoYWVzKHJhbmtpbmcsIGN1bXVsYXRpdmVfZ3AsIGxhYmVsID0gcGFzdGUwKHNjYWxlczo6bnVtYmVyKGN1bXVsYXRpdmVfZ3AsIGFjYyA9IDAuMSksICIlICIpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKGZvb2Rfd2FnZV9kZjIkY3VtdWxhdGl2ZV9ncCA8IDAsIDAsIDEpLCB2anVzdD0wLjc1LCBzaXplPTUpICsNCiAgZ2VvbV90ZXh0KGFlcyhyYW5raW5nLCBjdW11bGF0aXZlX2dwLCBsYWJlbCA9IHBhc3RlMChmb29kX29yX3dhZ2UsIiAiKSksDQogICAgICAgICAgICBoanVzdCA9IGlmZWxzZShmb29kX3dhZ2VfZGYyJGN1bXVsYXRpdmVfZ3AgPCAwLCAwLCAxKSwgdmp1c3Q9LTAuNzUsIHNpemU9NSwgZm9udGZhY2U9ImJvbGQiKSArDQogIGdlb21faW1hZ2UoYWVzKHJhbmtpbmcsIHk9LTQwLCBpbWFnZSA9IGltYWdlKSwNCiAgICAgICAgICAgICBoanVzdCA9IDAsIHNpemU9MC4wOCwpICsNCiAgbGFicyh0aXRsZT0nQ3VtdWxhdGl2ZSBGb29kIEluZmxhdGlvbiBhbmQgV2FnZSBHcm93dGggaW4gSW5kb25lc2lhJywNCiAgICAgICBzdWJ0aXRsZSA9ICcyMDEwLXtjbG9zZXN0X3N0YXRlfScsIHggPSAiIiwNCiAgICAgICB5ID0gIkN1bXVsYXRpdmUgUGVyY2VudGFnZSBDaGFuZ2UiLA0KICAgICAgIGNhcHRpb24gPSAnRGF0YSBTb3VyY2U6IGFwbGlrYXNpMi5wZXJ0YW5pYW4uZ28uaWQgJiBicHMuZ28uaWQuIEltYWdlIFNvdXJjZTogZnJlZXBpay5jb20uJykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAyNCksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDI0KSwNCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGNvbG9yPSdncmF5Jywgc2l6ZT0xMCksDQogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwxLDEsMSwgImNtIiksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkNCiAgKSArDQogIGd1aWRlcyhmaWxsID0gIm5vbmUiKQ0KDQphbmltYXRlZF9wbG90IDwtIHN0YXRpY19wbG90ICsgdHJhbnNpdGlvbl9zdGF0ZXMoeWVhciwgdHJhbnNpdGlvbl9sZW5ndGggPSA0LCBzdGF0ZV9sZW5ndGggPSAxKSArDQogIGVhc2VfYWVzKCdjdWJpYy1pbi1vdXQnKQ0KDQpnaWZfYW5pbWF0aW9uIDwtIGFuaW1hdGUoYW5pbWF0ZWRfcGxvdCwxMDAsZnBzID0gMjAsZHVyYXRpb24gPSAxMCwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSAxMDAwLCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpKQ0KZ2lmX2FuaW1hdGlvbg0KYW5pbV9zYXZlKCJmb29kX3dhZ2UuZ2lmIixhbmltYXRpb24gPSBnaWZfYW5pbWF0aW9uKQ0KYGBgDQoNCkZyb20gdGhlIHZpc3VhbGl6YXRpb24sIHdlIGNhbiBzZWUgdGhhdCB0aGUgbWluaW11bSB3YWdlIGluIEluZG9uZXNpYSBoYXMgYSBzaWduaWZpY2FudCBpbmNyZWFzZSBjb21wYXJlZCB0byB0aGUgYmFzaWMgZm9vZCBwcmljZXMgZnJvbSAyMDEwIHRvIDIwMjEuIFRoaXMgbWVhbnMgYSBtaW5pbXVtIHdhZ2Ugb24gMjAyMSBoYXMgbW9yZSBmb29kIHB1cmNoYXNpbmcgcG93ZXIgY29tcGFyZWQgdG8gMjAxMC4=